package top.byfree.jbyan.find;

import top.byfree.jbyan.find.exception.ParseArgException;
import top.byfree.jbyan.find.handler.Finder;
import top.byfree.jbyan.find.handler.ResultHandler;
import top.byfree.jbyan.find.result.ResultSet;
import top.byfree.jbyan.find.util.BinaryFileChecker;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static top.byfree.jbyan.find.FindApplication.ARGS;
import static top.byfree.jbyan.find.command.Arg.*;

/**
 * @author jbyan
 * @since 2023/8/4
 */
public class DirectoryProcessor {

    public final Finder finder;

    public final ArrayList<ResultHandler> resultHandlers;

    public DirectoryProcessor(Finder finder) {
        this.finder = finder;
        resultHandlers = new ArrayList<>();
    }

    public void addResultHandler(ResultHandler resultHandler) {
        resultHandlers.add(resultHandler);
    }

    public void processor() throws ParseArgException {
        String path = ARGS.getStringArg(DIRECTORY);
        int depth = ARGS.getIntArg(DEPTH);
        int pool = ARGS.getIntArg(THREAD_POOL);

        ExecutorService executorService = Executors.newFixedThreadPool(pool);
        Path dir = Paths.get(path);
        try {
            Files.walkFileTree(dir, EnumSet.noneOf(FileVisitOption.class), depth, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    // isBinaryFile 判断文件是否是二进制文件
                    if (!BinaryFileChecker.isBinaryFile(file) || ARGS.isArg(BINARY)) {
                        executorService.submit(new DirectoryRunnable(file.toAbsolutePath().toString(), finder, resultHandlers));
                    }
                    return FileVisitResult.CONTINUE;
                }
            });

            executorService.shutdown();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

class DirectoryRunnable implements Runnable {

    private final String filePath;

    private final Finder<ResultSet> finder;

    public final ArrayList<ResultHandler> resultHandlers;

    public DirectoryRunnable(String filePath, Finder<ResultSet> finder, ArrayList<ResultHandler> resultHandlers) {
        this.filePath = filePath;
        this.finder = finder;
        this.resultHandlers = resultHandlers;
    }

    @Override
    public void run() {
        Path path = Paths.get(filePath);

        try (BufferedReader reader = Files.newBufferedReader(path, Charset.forName(ARGS.getStringArg(ENCODING)))) {
            String line;
            int lineNumber = 0;
            // 维护一个存放结果的集合
            List<ResultSet> resultSets = new ArrayList<>();
            ResultSet result = finder.createResult();
            while ((line = reader.readLine()) != null) {
                lineNumber++;
                finder.find(line, lineNumber, result);
                // 找到一个结果
                if (result.isEnd()) {
                    // 添加到集合
                    resultSets.add(result);
                    // 构建一个新的结果
                    result = finder.createResult();
                }
            }

            for (ResultHandler resultHandler : resultHandlers) {
                resultHandler.processor(path, resultSets);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ParseArgException e) {
            System.out.println(e.getMessage());
            throw new RuntimeException(e);
        }

    }

}
